package com.hmdp.common.lock.Impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.common.lock.ILock;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * @author TJS
 * @Description  B站教学视频：https://www.bilibili.com/video/BV1cr4y1671t?p=60&vd_source=bda5ece524a5cc8f1f73003363c24712
 * @create 2023/1/3 22:19
 */
public class SimpleRedisLock implements ILock {

    private final StringRedisTemplate redisTemplate;
    public String name;

    /*
        泛型Long指的是返回值类型
     */
    private static final DefaultRedisScript<Long> REDIS_SCRIPT;

    static {
        REDIS_SCRIPT = new DefaultRedisScript<>();
        REDIS_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        REDIS_SCRIPT.setResultType(Long.class);
    }

    public SimpleRedisLock(StringRedisTemplate redisTemplate, String name) {
        this.redisTemplate = redisTemplate;
        this.name = name;
    }
    private final Long id = Thread.currentThread().getId();
    private final String ID_PREFIX = UUID.randomUUID().toString(true);

    @Override
    public Boolean tryLock(Long timeOutSec) {
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock:" + name, ID_PREFIX + id, timeOutSec, TimeUnit.SECONDS);
        /*
            避免包装类为null
         */
        return BooleanUtil.isTrue(lock);
    }
    /*
    @Override
    public void unLock() {
            在删除锁之前，获取锁的标识，确认删除的锁是自己之前设置的锁。避免锁的误删。
            缺点：由于以下逻辑不是原子性的，还是存在线程安全问题。
            TODO:分布式锁的原子性问题：https://www.bilibili.com/video/BV1cr4y1671t?p=61&vd_source=bda5ece524a5cc8f1f73003363c24712
        String content = redisTemplate.opsForValue().get("lock:" + name);
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        if(StrUtil.equals(content,threadId)){
            redisTemplate.delete("lock:" + name);
        }
    }
    */

    @Override
    public void unLock() {
        /*
          调用lua脚本，改造分布式锁。具有原子性。
         */
        redisTemplate.execute(
                REDIS_SCRIPT,
                Collections.singletonList("lock:" + name),//KEYS[1]
                ID_PREFIX + id  //AVGS[1]
        );
    }
}
